/**
 * @file Restrict.h
 * @author your name (you@domain.com)
 * @brief 
 * @version 0.1
 * @date 2020-10-18
 * 
 * @copyright Copyright (c) 2020
 * 
 */
#ifndef _RESTRICT_2D_H_
#define _RESTRICT_2D_H_

#include <iostream>
#include <vector>
#include <cmath>
#include "Miscellaneous.h"
namespace MyMultigrid_2D::Operator
{
    
    class RestrictOperator_2D
    {
    public:
        /* data */
        std::vector<double> input;
        std::vector<double> output;
    public:
        RestrictOperator_2D(){};
        RestrictOperator_2D(std::vector<double> v){input = v;};
        ~RestrictOperator_2D(){};
        virtual void DoRestrict(int l) = 0;
        virtual void SetInput(std::vector<double> v) = 0;
        virtual std::vector<double> ReturnRestricResult() = 0;
    };

    class FullWeightingRestrictOperator_2D:public RestrictOperator_2D
    {
        public:
        FullWeightingRestrictOperator_2D(){};
        FullWeightingRestrictOperator_2D(std::vector<double> v)
        {  
           FullWeightingRestrictOperator_2D::input = v;
        };
        void DoRestrict(int l)
        {
            int r2hsize = (pow(2,l-1) + 1);
            output.resize(r2hsize * r2hsize);
            for(int k = 0;k < output.size();k++)
            {
                if(isboundary(k,l - 1) == false)
                {
                    int i = index_i(k,l - 1);
                    int j = index_j(k,l - 1);
                    int v1 = index(2 * i - 1,2 * j - 1,l);
                    int v2 = index(2 * i - 1,2 * j + 1,l);
                    int v3 = index(2 * i + 1,2 * j - 1,l);
                    int v4 = index(2 * i + 1,2 * j + 1,l);
                    int v5 = index(2 * i,2 * j - 1,l);
                    int v6 = index(2 * i,2 * j + 1,l);
                    int v7 = index(2 * i - 1,2 * j,l);
                    int v8 = index(2 * i + 1,2 * j,l);
                    int v9 = index(2 * i,2 * j,l);
                    output[k] = (input[v1] + input[v2] + input[v3] + input[v4] + 2 *(input[v5] + input[v6] + input[v7] + input[v8]) + 4 * input[v9])/16.0;
                }
                else
                {
                    int i = index_i(k,l - 1);
                    int j = index_j(k,l - 1);
                    int v = index(2 * i, 2 * j,l);
                    output[k] = input[v]; 
                }
            }
            
        }
        void SetInput(std::vector<double> v){FullWeightingRestrictOperator_2D::input = v;};
        
        std::vector<double> ReturnRestricResult(){return output;};
    };

    class InjectionRestrictOperator_2D:public RestrictOperator_2D
    {
        public:
        InjectionRestrictOperator_2D(){};
        InjectionRestrictOperator_2D(std::vector<double> v)
        {
            InjectionRestrictOperator_2D::input = v;
        };
        void DoRestrict(int l)
        {
            int r2hsize = (pow(2,l-1) + 1);
            output.resize(r2hsize * r2hsize);
            for(int k = 0;k < output.size();k++)
            {

                int a = index_i(k,l - 1);
                int b = index_j(k,l - 1);
                int v = index(2 * a,2 * b,l);
                output[k] = input[v];

            }
        }
        void SetInput(std::vector<double> v){InjectionRestrictOperator_2D::input = v;};
        std::vector<double> ReturnRestricResult(){return output;};
    };   
}
#else
#endif